package org.zhangkun.jmsg.core.network;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import javax.swing.SwingUtilities;

import org.zhangkun.jmsg.bean.DataPack;
import org.zhangkun.jmsg.bean.DataPackage;
import org.zhangkun.jmsg.bean.DataUnPack;
import org.zhangkun.jmsg.bean.User;
import org.zhangkun.jmsg.core.UserManager;
import org.zhangkun.jmsg.core.listener.AcceptFileRequestListener;
import org.zhangkun.jmsg.core.network.exception.DataPackageParseException;
import org.zhangkun.jmsg.core.network.filetransport.FileMsgPackage;
import org.zhangkun.jmsg.core.network.filetransport.SendFiles;
import org.zhangkun.jmsg.util.ConfigManager;
import org.zhangkun.jmsg.util.GlobalVar;

/**
 * 接收和发出连接
 * 
 * @author zhangkun
 * 
 */
public class NetConnection {

	public class AcceptFileRequestThread extends Thread {

		private ArrayList<AcceptFileRequestListener> allLstener = new ArrayList<AcceptFileRequestListener>();
		private boolean stop = false;

		public AcceptFileRequestThread() {
			setName("AcceptFileRequestThread");
		}

		public void addListener(AcceptFileRequestListener afr) {
			allLstener.add(afr);
		}

		public void removeListener(AcceptFileRequestListener afr) {
			allLstener.remove(afr);
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			try {
				while (stop == false) {
					Socket socket = server.accept();
					// System.out.println("收到TCP连接:"
					// + socket.getInetAddress().getHostAddress() + ":"
					// + socket.getPort());
					boolean isColse = true;
					FileMsgPackage fmp = null;
					try {
						fmp = SendFiles.parseRequest(socket);
						if (fmp != null)
							for (int i = 0; i < allLstener.size(); i++) {
								if (allLstener.get(i).acceptFileRequest(socket,
										fmp)) {
									// allLstener.remove(i);
									isColse = false;
									break;
								}
							}
						// else
						// System.out.println("未读取数据!");
					} catch (DataPackageParseException e1) {
						// TODO Auto-generated catch block
						// System.out.println("数据解析错误!");
					}
					if (isColse)
						try {
							// System.out.println("无匹配项..");
							socket.close();
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				// e.printStackTrace();
			} finally {
				if (server != null)
					try {
						server.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}
	}

	public interface LoadingListener {
		void loading();

		void loadfinish();
	}

	private class AcceptRequestThread extends Thread {

		private boolean stop = false;

		public AcceptRequestThread() {
			setName("AcceptRequestThread");
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			loadingListener.loading();
			landing();
			loadingListener.loadfinish();
			loadingListener = null;
			while (stop == false) {
				try {
					byte[] buffer = new byte[65535];
					DatagramPacket dp = new DatagramPacket(buffer,
							buffer.length);
					serverSocket.receive(dp);
					// 如果传输队列满了则等待处理完毕在接收
					while (stop == false) {
						if (dataPacket.size() < GlobalVar.MAX_TRANSPORT_QUEUE) {
							synchronized (dataPacket) {
								dataPacket.add(dp);
								break;
							}
						} else {
							try {
								Thread.sleep(200);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					System.out.println("关闭连接");
					// e.printStackTrace();
					break;
				}
			}
			System.out.println("AcceptRequestThread in exit");
			manageRequestDataThread.setStop(true);
			while (manageRequestDataThread.isAlive())
				;
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}
	}

	private class ManageRequestDataThread extends Thread {

		public ManageRequestDataThread() {
			setName("ManageRequestDataThread");
		}

		private boolean stop = false;

		public byte[] clear(byte[] b) {
			int i = -1;
			int j = b.length - 1;
			while (j > 0 && (i = b[j]) == 0)
				j--;
			for (i = 0; i < b.length; i++)
				if (b[i] == 0)
					b[i] = 46;
			byte[] rb = new byte[j + 1];
			System.arraycopy(b, 0, rb, 0, rb.length);
			return rb;
		}

		public void run() {
			while (stop == false) {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				DatagramPacket dp = null;
				synchronized (dataPacket) {
					if (dataPacket.size() != 0) {
						dp = dataPacket.get(0);
						dataPacket.remove(0);
					}
				}

				if (dp != null) {
					// dup = new DataUnPack();
					final DataUnPack dup = new DataUnPack();
					try {
						// System.out.print("清理前");
						// print(dp.getData());
						dp.setData(clear(dp.getData()));
						// System.out.print("\n清理后");
						// print(dp.getData());
						dup.unPack(dp, GlobalVar.TRANSPORT_ENCODING);
						String data = "";
						try {
							data = new String(dp.getData(),
									GlobalVar.TRANSPORT_ENCODING);
						} catch (UnsupportedEncodingException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						// System.out.print("Data ");
						// for (int i = 0; i < dp.getData().length; i++) {
						// System.out.print(Integer.toHexString(
						// dp.getData()[i]).toUpperCase()
						// + " ");
						// }
						// System.out.println("");
						System.out.println("收到数据包( from:"
								+ dup.getSenderHostAddres() + ":"
								+ dp.getPort() + " Data:" + data + ")");
						try {
							SwingUtilities.invokeAndWait(new Runnable() {
								@Override
								public void run() {
									// TODO Auto-generated method stub
									responseDataPackage.responce(dup);
								}
							});
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (InvocationTargetException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					} catch (DataPackageParseException e) {
						// TODO Auto-generated catch block
						// System.out.println("解析错误( from:"
						// + dp.getAddress().getHostAddress() + " Data:"
						// + new String(dp.getData()) + ")");
					}
				}
			}
			System.out.println("ManageRequestDataThread in exit");
		}

		public void setStop(boolean stop) {
			this.stop = stop;
		}
	}

	public final static NetConnection instance = new NetConnection();

	/**
	 * 监听文件传输请求
	 */
	private AcceptFileRequestThread acceptFileRequestThread = new AcceptFileRequestThread();
	/**
	 * 等待UDP数据
	 */
	private AcceptRequestThread acceptRequestThread = new AcceptRequestThread();

	/**
	 * 数据包对象
	 */
	private ArrayList<DatagramPacket> dataPacket = new ArrayList<DatagramPacket>();
	/**
	 * 处理UDP数据包
	 */
	private ManageRequestDataThread manageRequestDataThread = new ManageRequestDataThread();

	/**
	 * 响应消息的对象
	 */
	private ResponseDataPackage responseDataPackage;

	/**
	 * 等待消息回复
	 */

	private ServerSocket server = null;

	/**
	 * UDP端口监听
	 */
	private DatagramSocket serverSocket;

	/**
	 * 可用的IP地址
	 */
	private ArrayList<String> usableAddres = new ArrayList<String>();

	private NetConnection() {
		this.responseDataPackage = new ResponseDataPackage();
	}

	public long cancelFileTransport(String ip, String packageID) {
		// TODO Auto-generated method stub
		DataPack dp = new DataPack();
		dp.setDataType(DataPackage.IPMSG_RELEASEFILES);
		dp.setMsg(packageID);
		try {
			this.sendBroadcast(ip, dp.getData(GlobalVar.TRANSPORT_ENCODING));
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		return Long.parseLong(dp.getDataID());
	}

	public void close() {
		if (this.serverSocket != null)
			exitLanding();
		acceptRequestThread.setStop(true);
		acceptFileRequestThread.setStop(true);
		if (server != null)
			try {
				server.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		if (serverSocket != null)
			serverSocket.close();
		System.out.println("wait acceptRequestThread finish...");
		while (acceptRequestThread.isAlive())
			;
		while (acceptFileRequestThread.isAlive())
			;
		System.out.println("JMsg exit.");
	}

	/**
	 * 广播退出
	 * 
	 */
	public void exitLanding() {// 发送广播
		DataPack dp = new DataPack();
		for (int i = 0; i < UserManager.instance.getAllUser().size(); i++) {
			String ip = UserManager.instance.getAllUser().get(i).getIp();
			dp.setDataType(DataPack.IPMSG_NOOPERATION);
			try {
				sendBroadcast(ip, dp.getData(GlobalVar.TRANSPORT_ENCODING));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			dp.setDataType(DataPack.IPMSG_BR_EXIT);
			try {
				sendBroadcast(ip, dp.getData(GlobalVar.TRANSPORT_ENCODING));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public AcceptFileRequestThread getAcceptFileRequestThread() {
		return acceptFileRequestThread;
	}

	public String getAddresStr(byte[] addres) {
		String ret = "";
		for (int i = 0; i < addres.length; i++) {
			ret += (int) (addres[i] < 0 ? 256 + addres[i] : addres[i]);
			if (i != addres.length - 1)
				ret += ".";
		}
		return ret;
	}

	public String getLocalHostAddres() {
		try {
			return InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public ResponseDataPackage getResponseDataPackage() {
		return responseDataPackage;
	}

	public String getSubnetMask(NetworkInterface netCard) {
		int prefix = 0;
		int[] ipSplit = new int[16];
		String subnetMask = null;
		List<InterfaceAddress> localInterface = null;
		localInterface = netCard.getInterfaceAddresses();
		Iterator<InterfaceAddress> iterator = null;
		iterator = localInterface.iterator();
		while (iterator.hasNext()) {
			InterfaceAddress temp = null;
			temp = iterator.next();
			prefix = temp.getNetworkPrefixLength();
		}
		System.out.println(prefix);
		int index = 0;
		int split = 0;
		int remainder = 0;
		split = prefix / 8;
		System.out.println(split);
		remainder = prefix % 8;
		while (index < split) {
			ipSplit[index] = 255;
			index++;
		}
		if (remainder == 1)
			ipSplit[index] = 128;
		if (remainder == 2)
			ipSplit[index] = 192;
		if (remainder == 3)
			ipSplit[index] = 224;
		if (remainder == 4)
			ipSplit[index] = 240;
		if (remainder == 5)
			ipSplit[index] = 248;
		if (remainder == 6)
			ipSplit[index] = 252;
		if (remainder == 7)
			ipSplit[index] = 254;
		index++;
		while (index < remainder) {
			ipSplit[index] = 0;
			index++;
		}
		subnetMask = String.valueOf(ipSplit[0]) + "."
				+ String.valueOf(ipSplit[1]) + "." + String.valueOf(ipSplit[2])
				+ "." + String.valueOf(ipSplit[3]);
		return subnetMask;
	}

	private LoadingListener loadingListener;

	/**
	 * 初始化网络信息
	 * 
	 * @throws IOException
	 */
	public void init(LoadingListener loadingListener) throws IOException {
		ConfigManager.loadConfig();
		this.loadingListener = loadingListener;
		serverSocket = new DatagramSocket(GlobalVar.IPMSG_PORT);
		server = new ServerSocket(GlobalVar.IPMSG_PORT);
		MsgTransport.instance.start();
		try {
			UserManager.me
					.setHostName(InetAddress.getLocalHost().getHostName());
			UserManager.me.setIp(InetAddress.getLocalHost().getHostAddress());
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		UserManager.me.setGroup(GlobalVar.GROUP);
		UserManager.me.setUsername(System.getProperty("user.name", "NoName"));
		UserManager.me.setName(GlobalVar.NAME);
		UserManager.me.setVersion(User.CLIENT_JMSG);

		UserManager.instance.addUser(UserManager.me);
		// 开启等待UDP
		acceptRequestThread.setPriority(Thread.MAX_PRIORITY);
		acceptRequestThread.start();
		// 处理请求数据
		manageRequestDataThread.start();
		// 处理传送请求
		acceptFileRequestThread.start();

	}

	public boolean isUsableAddres(String addres) {
		for (String a : usableAddres)
			if (addres.equals(a))
				return true;
		return false;
	}

	/**
	 * 广播登陆
	 */
	public void landing() {// 发送广播
		DataPack dp = new DataPack();
		dp.setDataType(DataPack.IPMSG_BR_ENTRY);
		sendAllBroadcast(dp, UserManager.instance.getAllUser());
	}

	/**
	 * 广播登陆
	 */
	public void landing(ArrayList<User> allUser) {// 发送广播
		DataPack dp = new DataPack();
		dp.setDataType(DataPack.IPMSG_BR_ENTRY);
		sendAllBroadcast(dp, allUser);
	}

	/**
	 * 广播在线
	 */
	public void refresh(ArrayList<User> allUser) {// 发送广播
		DataPack dp = new DataPack();
		dp.setDataType(DataPack.IPMSG_NOOPERATION);
		sendAllBroadcast(dp, allUser);
	}

	private class ScanThread extends Thread {
		private String ip;
		private boolean waiting = true;
		private boolean stop = false;
		private DataPack dp;

		public ScanThread(DataPack dp) {
			this.dp = dp;
			start();
		}

		public void setIp(String ip) {
			this.ip = ip;
		}

		public void run() {
			while (!stop) {
				if (waiting) {
					try {
						Thread.sleep(1);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} else {
					try {
						sendBroadcast(ip,
								dp.getData(GlobalVar.TRANSPORT_ENCODING));
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					waiting = true;
				}
			}
		}
	}

	public void updateUseableAddres() {

		usableAddres.clear();
		try {
			Enumeration<NetworkInterface> e = NetworkInterface
					.getNetworkInterfaces();
			while (e.hasMoreElements()) {
				NetworkInterface networkInterface = e.nextElement();
				if (!networkInterface.isUp())
					continue;
				Enumeration<InetAddress> ip = networkInterface
						.getInetAddresses();
				while (ip.hasMoreElements()) {
					InetAddress ia = ip.nextElement();
					if (ia.isSiteLocalAddress()) {
						if (!usableAddres.contains(ia.getHostAddress()))
							usableAddres.add(ia.getHostAddress());
					}
				}
			}
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 取得所有可用IP地址。
	 * 
	 * @return
	 */
	public ArrayList<String> getAllIP() {
		// 取得已经存在的IP地址
		updateUseableAddres();
		for (String a : usableAddres) {
			String scanip = getScanIp(a);
			if (!GlobalVar.SCAN_IP.contains(scanip))
				GlobalVar.SCAN_IP.add(scanip);
		}
		ArrayList<String> existsIP = new ArrayList<String>();
		for (int i = 0; i < UserManager.instance.getAllUser().size(); i++) {
			existsIP.add(UserManager.instance.getAllUser().get(i).getIp());
		}
		// 向其他网段发送
		for (int i = 0; i < GlobalVar.SCAN_IP.size(); i++) {
			String[] ips = GlobalVar.SCAN_IP.get(i).split(",");
			System.out.println("扫描：" + GlobalVar.SCAN_IP.get(i));
			String tmp = ips[0];
			int start = Integer.parseInt(ips[1]);
			int end = Integer.parseInt(ips[2]);
			for (int j = start; j <= end; j++) {
				// 跳过已经发送过的IP
				if (existsIP.contains(tmp + j))
					continue;
				else
					existsIP.add(tmp + j);
			}
		}
		return existsIP;
	}

	/**
	 * 向所有网络IP地址发送广播
	 */
	public void sendAllBroadcast(DataPack dp, ArrayList<User> allUser) {
		// try {
		// sendBroadcast("255.255.255.255", dp.getData("gbk"));
		// } catch (Exception e1) {
		// // TODO Auto-generated catch block
		// e1.printStackTrace();
		// }
		System.out.println("启动发送线程");
		// 启动发送线程
		ArrayList<ScanThread> allSendThread = new ArrayList<ScanThread>();
		for (int i = 0; i < GlobalVar.SCAN_THREADS; i++)
			allSendThread.add(new ScanThread(dp));

		ArrayList<String> ips = getAllIP();

		// 发送
		for (int i = 0; i < ips.size(); i++) {
			System.out.println("扫描：" + ips.get(i));
			boolean send = false;
			while (!send) {
				for (int z = 0; z < allSendThread.size(); z++) {
					ScanThread st = allSendThread.get(z);
					if (st.waiting) {
						st.setIp(ips.get(i));
						st.waiting = false;
						send = true;
						break;
					}
				}
			}
		}
		System.out.println("等待线程退出");
		for (int i = 0; i < allSendThread.size(); i++) {
			ScanThread st = allSendThread.get(i);
			st.stop = true;
			while (st.isAlive())
				;
		}
		System.out.println("完成");
	}

	public String getScanIp(String ip) {
		return ip.substring(0, ip.lastIndexOf(".")) + ".," + "1" + "," + "254";
	}

	/**
	 * 发送数据包到指定地址
	 * 
	 * @param dp
	 */
	public void sendBroadcast(DataPack dp) {
		try {
			sendBroadcast(dp.getSendToHostAddres(),
					dp.getData(GlobalVar.TRANSPORT_ENCODING));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void sendBroadcast(String addres, byte[] data) throws Exception {
		System.out.println("发送数据包( To  :" + addres + ":" + GlobalVar.IPMSG_PORT
				+ " Data:" + new String(data, GlobalVar.TRANSPORT_ENCODING)
				+ ")");
		// DatagramSocket socket;
		DatagramPacket packet;
		// socket = new DatagramSocket();
		// socket.setBroadcast(true); // 有没有没啥不同
		// send端指定接受端的端口，自己的端口是随机的
		packet = new DatagramPacket(data, data.length,
				InetAddress.getByName(addres), GlobalVar.IPMSG_PORT);
		serverSocket.send(packet);
		// socket.close();
	}

	public ArrayList<String> getUsableAddres() {
		return usableAddres;
	}

	public void setUsableAddres(ArrayList<String> usableAddres) {
		this.usableAddres = usableAddres;
	}

	public long sendFile(String ip, String msg) {
		// TODO Auto-generated method stub
		DataPack dp = new DataPack();
		dp.setDataType(DataPackage.IPMSG_SENDMSG
				| DataPackage.IPMSG_SENDCHECKOPT
				| DataPackage.IPMSG_FILEATTACHOPT);
		dp.setMsg(msg);
		try {
			this.sendBroadcast(ip, dp.getData(GlobalVar.TRANSPORT_ENCODING));
		} catch (Exception e) {
			// TODO Auto-generated catch block
		}
		return Long.parseLong(dp.getDataID());
	}

	public String sendMsg(String ip, String text, long op) {
		DataPack dp = new DataPack();
		dp.setDataType(op);
		dp.setMsg(text);
		try {
			this.sendBroadcast(ip, dp.getData(GlobalVar.TRANSPORT_ENCODING));
			return dp.getDataID();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			return null;
		}
	}

	public void sendNoRevertMsg(String ip, String text) {
		// TODO Auto-generated method stub
		sendMsg(ip, text, DataPackage.IPMSG_SENDMSG);
	}

	public void setResponseDataPackage(ResponseDataPackage responseDataPackage) {
		this.responseDataPackage = responseDataPackage;
	}

}
